两周前因工作需要开始入门Objective-C,第一个任务是基于一个deprecated open source library (AudioSessionManager) 写一个VoIP程序中切换音频出声道的功能。
先是花了一点时间研究了一下iPhone自带电话的native behaviour...
iPhone有两个出声道built-in receiver和built-in speaker.一般打电话用的是built-in receiver.当用户选择了Speaker的时候出声道自动转到built-in peaker.
当用户插入耳机以后,出声道会跳转至priority更高一级的headphones.
当还有其他蓝牙音箱或是耳机连接的时候,原本的Speaker按钮会变成Speaker/Bluetooth按钮(图片上多了一个蓝牙符号).如果用户选择了蓝牙,声音会从蓝牙设备中传出.
所以四种出声轨道,按照priority排序分别是built-in speaker (只在用户特别选择时才active) < built-in receiver (default) < headphones = bluetooth (耳机和蓝牙的priority取决于谁先连接,如果耳机最后连接,默认出声道是耳机.如果最后连接的是蓝牙,则蓝牙是默认出声道.)
接下来是研究了一下现有的Libarary... 苹果有两个相关的Library: Audio Session Services, AVAudioSession.在最新的WWDC里,苹果把基于C语言的AudioSession给deprecated了,取而代之的是更完善的AVAudioSession.说是更完善但其实很多东西也不是那么直接明了的就有的。我一开始还指望它能帮我detect有没有除了built-in以外的其他出声道.而实际上对于出声道的信息并不是很直白,我花了好长时间才搞出个大概来...
首先,在使用AVAudioSession之前要做一个configuration.这包括了category,mode,还有additional option for bluetooth.
因为是一个具有录音功能的VoIP app所以我需要选择的Category是PlayAndRecord,选择的Mode是VoiceChat.StackOverflow上有一些古早的方法支持蓝牙,用的是AudioSession services里面的AudioSessionSetProperty.相比之下AVAudioSession的setCategory:withOptions要简洁的多:
[audioSession setCategory:audioCat withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&err]
一行code基本就可以让这个app按照default priority自动选择出声道了.下一个问题是,如何能够让用户自由选择.我大致分了四个起始状态:
1. default (receiver/speaker)
2. with headphones (headphone/speaker)
3. with bluetooth (receiver/speaker/bluetooth)
4. with all (headphone/speaker/bluetooth)
第一、二种情况没什么问题,耳机的部分是自动优先的。切换Speaker的话,只是从receiver变成speaker,或者反过来,用的是override:
// receiver to speaker
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&err];
// speaker to receiver
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
第三、四种情况是类似的,重点是搞定蓝牙设备.开始的时候一直在纠结两个问题:1)给用户提供所有available audio options然后可任其切换; 2)当蓝牙出现或消失的时候,toggle蓝牙按钮.
// 休息一下,我等会接下去...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。